﻿

#region Using Statements

// System References
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

// XNA References
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

// Internal References
using Freeform.Framework;

#endregion


namespace Freeform.Framework.Graphics
{
    /// <summary>
    /// The animation set allows an entity to have a variety of different animations and controls the playing of these.
    /// </summary>
    public class AnimationSet
    {

        #region Events

        /// <summary>
        /// This event is fired whenever an animation has finished playing.
        /// </summary>
        public event AnimationFinishedEventHandler AnimationFinished;
        public delegate void AnimationFinishedEventHandler(AnimationSet set, Animation animation);

        /// <summary>
        /// Fires the Animation Finished event.
        /// </summary>
        /// <param name="animation">The animation that has finished.</param>
        private void OnAnimationFinished(Animation animation)
        {
            animation.AnimationFinished -= OnAnimationFinished;
            if (AnimationFinished != null)
                AnimationFinished(this, animation);
        }

        #endregion


        #region Members

        /// <summary>
        /// A set of animations that belong to this entity.
        /// </summary>
        protected virtual Dictionary<String, Animation> Animations { get; set; }

        /// <summary>
        /// Gets the currently playing animation.
        /// </summary>
        public virtual Animation Current { get; private set; }

        #endregion


        #region Init

        /// <summary>
        /// Creates a new empty animation set.
        /// </summary>
        public AnimationSet() { }


        /// <summary>
        /// Creates a new animation set.
        /// </summary>
        /// <param name="animation">An animation to add to the set.</param>
        public AnimationSet(Animation Animation)
        {
            AddAnimation(Animation);
        }


        /// <summary>
        /// Creates a new unanimated image with the given texture name.
        /// </summary>
        /// <param name="TextureName">The name of the texture.</param>
        public AnimationSet(String TextureName)
        {
            AddAnimation(new Animation(TextureName));
        }

        #endregion


        #region Utility

        /// <summary>
        /// Adds an animation to the animation set.
        /// </summary>
        /// <param name="animation">The animation to be added.</param>
        public virtual void AddAnimation(Animation animation)
        {
            /*  If the current animation is null then the animation set is empty.
             *  In this case we simply add the animation to current without incurring
             *  the memory overhead of instantiating a full animation dictionary.
             *  This is beneficial because many entities only have one animation. */
            if (Current == null)
                Current = animation;

            /*  If the current animation wasn't null but the dictionary is null,
             *  we need to create a new dictionary because we are adding a second
             *  animation. We add the old and the new animation to the dictionary. */
            else if (Animations == null)
            {
                Animations = new Dictionary<string, Animation>();
                Animations.Add(Current.AnimationName, Current);
                Animations.Add(animation.AnimationName, animation);
            }

            /*  Otherwise we are simply adding another animation to the dictionary. */
            else
                Animations.Add(animation.AnimationName, animation);
        }

        /// <summary>
        /// An extension on AddAnimation designed to allow an animation to freeze on a particular
        /// frame for the sake of using Draw_Source to draw only a certain part of an image without
        /// animating.
        /// </summary>
        /// <param name="animation"></param>
        public virtual void AddAnimation(Animation animation, int PauseFrame)
        {
            /*  If the current animation is null then the animation set is empty.
             *  In this case we simply add the animation to current without incurring
             *  the memory overhead of instantiating a full animation dictionary.
             *  This is beneficial because many entities only have one animation. */
            if (Current == null)
                Current = animation;

            /*  If the current animation wasn't null but the dictionary is null,
             *  we need to create a new dictionary because we are adding a second
             *  animation. We add the old and the new animation to the dictionary. */
            else if (Animations == null)
            {
                Animations = new Dictionary<string, Animation>();
                Animations.Add(Current.AnimationName, Current);
                Animations.Add(animation.AnimationName, animation);
            }

            /*  Otherwise we are simply adding another animation to the dictionary. */
            else
                Animations.Add(animation.AnimationName, animation);
        }

        /// <summary>
        /// Removes an animation from the animation set.
        /// </summary>
        /// <param name="animationName"></param>
        public virtual void RemoveAnimation(String animationName)
        {
            // Stop current animation from playing if it is the animation to be removed.
            if (Current.AnimationName.CompareTo(animationName) == 0)
                Current = null;

            // If the dictionary exists, remove the animation from there to.
            if (Animations != null)
                Animations.Remove(animationName);
        }


        /// <summary>
        /// Starts playing an animation.
        /// </summary>
        /// <param name="animationName"></param>
        public virtual void PlayAnimation(String animationName)
        {
            if (Current.AnimationName.CompareTo(animationName) == 0) { }
            else if (Animations != null)
            {
                Animation animation;
                if (Animations.TryGetValue(animationName, out animation))
                    Current = animation;
                else
                    throw new Exception("Could not locate animation: " + animationName);
            }
            else return;
            Current.Play();
            Current.AnimationFinished += OnAnimationFinished;
        }

        /// <summary>
        /// Gets the animation of the specified name.
        /// </summary>
        /// <param name="animationName">The name of the animation to find.</param>
        /// <returns>The animation or null if the animation does not exist.</returns>
        public virtual Animation GetAnimation(String animationName)
        {
            // Stop current animation from playing if it is the animation to be removed.
            if (Current.AnimationName.CompareTo(animationName) == 0)
                return Current;

            // If the dictionary exists, remove the animation from there to.
            Animation Result = null;
            if (Animations != null)
                Animations.TryGetValue(animationName, out Result);

            return Result;
        }

        #endregion

    }
}
